///\todo upgrade

//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_GEOMETRY_CPOINT_H_
#define _BUOLA_GEOMETRY_CPOINT_H_

#include <buola/buola.h>
#include <buola/io.h>

namespace buola {

///\addtogroup geometry
///@{

namespace detail {

    template<typename tFrom,typename tTo>
    struct QAutomaticPointConversion
    {
        static const bool value=false;
    };

    template<>
    struct QAutomaticPointConversion<int,double>
    {
        static const bool value=true;
    };
    
    template<>
    struct QAutomaticPointConversion<int,float>
    {
        static const bool value=true;
    };

    template<>
    struct QAutomaticPointConversion<float,double>
    {
        static const bool value=true;
    };

/*namespace detail*/ }

////////////////////////////////////////////////////////////////////////////

/// 2D point

/// represents a point in 2 dimensions
///
////////////////////////////////////////////////////////////////////////////

template<typename tType>
class CPoint
{
public:
    typedef tType TType;

///\name constructors and destructor
//@{
    ///default constructor (leaves uninitialized)
    CPoint()=default;

    ///copy constructor
    CPoint(const CPoint&)=default;

    ///from point of different type
    template<typename tO,OEnableIf<detail::QAutomaticPointConversion<tO,tType>> = 0>
    CPoint(const CPoint<tO> &pO)
        :   x(undefined(pO.x)?undefined<tType>():(tType)pO.x)
        ,   y(undefined(pO.y)?undefined<tType>():(tType)pO.y)
    {}

    template<typename tO,OEnableIfNot<detail::QAutomaticPointConversion<tO,tType>> = 0>
    explicit CPoint(const CPoint<tO> &pO)
        :   x(undefined(pO.x)?undefined<tType>():(tType)pO.x)
        ,   y(undefined(pO.y)?undefined<tType>():(tType)pO.y)
    {}


    ///from coordinates
    CPoint(tType px,tType py)
        :   x(px)
        ,   y(py)
    {}

    ///undefined constructor
    CPoint(NUndefined)
        :   x(undefined<tType>())
        ,   y(undefined<tType>())
    {}

    ///zero constructor
    CPoint(NZero)
        :   x(0)
        ,   y(0)
    {}
//@}

    ///assignment operator
    CPoint &operator=(const CPoint&)=default;

    //! subtracts two points coordinate-wise (in-place version)
    CPoint &operator-=(const CPoint &pRH)
    {
        x-=pRH.x;
        y-=pRH.y;
        return *this;
    }

    //! adds two points coordinate-wise (in-place version)
    CPoint &operator+=(const CPoint &pRH)
    {
        x+=pRH.x;
        y+=pRH.y;
        return *this;
    }

    //! multiplies a point by a scalar (in-place version)
    CPoint &operator*=(tType pRH)
    {
        x*=pRH;
        y*=pRH;
        return *this;
    }

    //! divides a point by a scalar (in-place version)
    CPoint &operator/=(tType pRH)
    {
        x/=pRH;
        y/=pRH;
        return *this;
    }

    //! comparison operator
    bool operator==(const CPoint &pOther) const
    {
        return (x==pOther.x&&y==pOther.y);
    }

    //! inequality operator
    bool operator!=(const CPoint &pOther) const
    {
        return (x!=pOther.x||y!=pOther.y);
    }

    bool operator<(const CPoint &pOther) const
    {
        return (x<pOther.x&&y<pOther.y);
    }

    bool operator>(const CPoint &pOther) const
    {
        return (x>pOther.x&&y>pOther.y);
    }

    bool operator<=(const CPoint &pOther) const
    {
        return (x<=pOther.x&&y<=pOther.y);
    }

    bool operator>=(const CPoint &pOther) const
    {
        return (x>=pOther.x&&y>=pOther.y);
    }

    //! returns true if the point is undefined
    bool Undefined() const
    {
        return (undefined(x)||undefined(y));
    }

    tType x;   //!< x coordinate
    tType y;   //!< y coordinate
};

///\relates CPoint
///\brief negates each coordinate of the point
template<typename tType>
inline CPoint<tType> operator-(const CPoint<tType> &pPoint)
{
    return CPoint<tType>(-pPoint.x,-pPoint.y);
}

///\relates CPoint
///\brief adds two points coordinate-wise
template<typename tType>
inline CPoint<tType> operator+(const CPoint<tType> &pLH,const CPoint<tType> &pRH)
{
    return CPoint<tType>(pLH.x+pRH.x,pLH.y+pRH.y);
}

//! \relates CPoint
//! \brief subtracts two points coordinate-wise
template<typename tType>
inline CPoint<tType> operator-(const CPoint<tType> &pLH,const CPoint<tType> &pRH)
{
    return CPoint<tType>(pLH.x-pRH.x,pLH.y-pRH.y);
}

//! \relates CPoint
//! \brief adds a scalar to each coordinate of the point
template<typename tType>
inline CPoint<tType> operator+(const CPoint<tType> &pLH,double pRH)
{
    return CPoint<tType>(pLH.x+pRH,pLH.y+pRH);
}

//! \relates CPoint
//! \brief subtracts a scalar from each coordinate of the point
template<typename tType>
inline CPoint<tType> operator-(const CPoint<tType> &pLH,double pRH)
{
    return CPoint<tType>(pLH.x-pRH,pLH.y-pRH);
}

//! \relates CPoint
//! \brief multiplies a point by a scalar
template<typename tType>
inline CPoint<tType> operator*(const CPoint<tType> &pLH,double pRH)
{
    return CPoint<tType>(pLH.x*pRH,pLH.y*pRH);
}

//! \relates CPoint
//! \brief multiplies a point by a scalar
template<typename tType>
inline CPoint<tType> operator*(double pLH,const CPoint<tType> &pRH)
{
    return CPoint<tType>(pLH*pRH.x,pLH*pRH.y);
}

//! \relates CPoint
//! \brief divides a point by a scalar
template<typename tType>
inline CPoint<tType> operator/(const CPoint<tType> &pLH,double pRH)
{
    return CPoint<tType>(pLH.x/pRH,pLH.y/pRH);
}

///\relates CPoint
///\brief returns the norm of the point
template<typename tType>
inline tType norm(const CPoint<tType> &pPoint)
{
    return hypot(pPoint.x,pPoint.y);
}

///\relates CPoint
///\brief distance between two points
template<typename tType>
inline tType dist(const CPoint<tType> &pLH,const CPoint<tType> &pRH)
{
    return norm(pLH-pRH);
}

///\relates CPoint
///\brief manhattan distance between two points
template<typename tType>
inline tType manhattan_dist(const CPoint<tType> &pLH,const CPoint<tType> &pRH)
{
    return std::abs(pLH.x-pRH.x)+std::abs(pLH.y-pRH.y);
}

//! \relates CPoint
//! \brief returns the dot product of the two points
template<typename tType>
inline double dot(const CPoint<tType> &pLH,const CPoint<tType> &pRH)
{
    return pLH.x*pRH.x+pLH.y*pRH.y;
}

///\relates CPoint
///\brief output operator
template<typename tType>
inline io::CTextWriter &operator<<(io::CTextWriter &pWriter,const CPoint<tType> &pPoint)
{
    pWriter << '(' << pPoint.x << ',' << pPoint.y << ')';
    return pWriter;
}

///\relates CPoint
///\brief input operator
template<typename tChar,typename tType>
inline io::CTextReader &operator>>(io::CTextReader &pReader,CPoint<tType> &pPoint)
{
    char lDummy;
    pReader >> lDummy >> pPoint.x >> lDummy >> pPoint.y >> lDummy;
    return pReader;
}

typedef CPoint<double> CPoint_d;
typedef CPoint<float> CPoint_f;
typedef CPoint<int> CPoint_i;

template<typename tSerializer,typename tType>
inline void serialize(tSerializer &pS,const CPoint<tType> &pP)
{
    pS << pP.x << pP.y;
}

template<typename tSerializer,typename tType>
inline void deserialize(tSerializer &pS,CPoint<tType> &pP)
{
    pS >> pP.x >> pP.y;
}

///@}

/*namespace buola*/ }

#endif
